﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Template.Infrastructure.Extensions;

namespace Template.Infrastructure.ExpressionUtil
{
  public   static class FilterExpression
    {

        //private static ParameterExpression parameter = null;

        public static Expression<Func<T, bool>> ToExpression<T>(IEnumerable<FilterInfo> filterInfos)
        {
            var parameter= Expression.Parameter(typeof(T)); 
            filterInfos = filterInfos.Where(o => o.IsEmptyOrNull == false && !(o.Value.IsNullOrWhiteSpace()));
            var query = ParseExpressionBody(filterInfos, parameter);
            var lambda = Expression.Lambda<Func<T, bool>>(query, parameter);
            return lambda;
        }
        private static Expression  ParseExpressionBody(IEnumerable<FilterInfo> filterInfos, ParameterExpression parameter)
        {

            if (filterInfos == null || filterInfos.Count() == 0)
            {
                return Expression.Constant(true, typeof(bool));
            }
            else if (filterInfos.Count() == 1)
            {
                return ParseCondition(filterInfos.First(), parameter);
            }
            else
            {
                var first = filterInfos.First();
                var skip = filterInfos.Skip(1);
                Expression left = ParseCondition(first, parameter);
                Expression right = ParseExpressionBody(skip, parameter);
                if (first.Connect == FilterConnect.And)
                {
                    return Expression.AndAlso(left, right);
                }
                else
                {
                    return Expression.OrElse(left, right);
                }

            }
        }

        //private static Dictionary<UosoOperatorEnum, Func<Expression, Expression, Expression>> ExpressionDic =
        //     new Dictionary<UosoOperatorEnum, Func<Expression, Expression, Expression>>()
        //     {

        //         [UosoOperatorEnum.Equal] = Expression.Equal,
        //         [UosoOperatorEnum.Greater] = Expression.GreaterThan,
        //         [UosoOperatorEnum.GreaterEqual] = Expression.GreaterThanOrEqual,
        //         [UosoOperatorEnum.Less] = Expression.LessThan,
        //         [UosoOperatorEnum.LessEqual] = Expression.LessThanOrEqual,
        //         [UosoOperatorEnum.NotEqual] = Expression.NotEqual,
        //     };



        private static Expression  ParseCondition(FilterInfo filterInfo, ParameterExpression p)
        {
            Expression key = Expression.Property(p, filterInfo.Key);
            Expression value = Expression.Constant(filterInfo.Value);
            switch (filterInfo.Operator)
            {
                case FilterOperator.Contains:
                    return Expression.Call(key, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), value);
                case FilterOperator.Equal:
                    return Expression.Equal(key, Expression.Convert(value, key.Type));
                case FilterOperator.GreaterThan:
                    return Expression.GreaterThan(key, Expression.Convert(value, key.Type));
                case FilterOperator.GreaterThanOrEqual:
                    return Expression.GreaterThanOrEqual(key, Expression.Convert(value, key.Type));
                case FilterOperator.LessThan:
                    return Expression.LessThan(key, Expression.Convert(value, key.Type));
                case FilterOperator.LessThanOrEqual:
                    return Expression.LessThanOrEqual(key, Expression.Convert(value, key.Type));
                case FilterOperator.NotEqual:
                    return Expression.NotEqual(key, Expression.Convert(value, key.Type));
                case FilterOperator.In:
                    return ParaseIn(p, filterInfo);
                case FilterOperator.Between:
                    return ParaseBetween(p, filterInfo);
                default:
                    throw new NotImplementedException("不支持此操作");
            }
        }




        /// <summary>
        /// Between
        /// </summary>
        /// <param name="parameter"></param>
        /// <param name="filterInfo"></param>
        /// <returns></returns>
        private static Expression ParaseBetween(ParameterExpression parameter, FilterInfo filterInfo)
        {
            ParameterExpression p = parameter;
            Expression key = Expression.Property(p, filterInfo.Key);
            var valueArr = filterInfo.Value.Split(',');
            if (valueArr.Length != 2)
            {
                throw new NotImplementedException("ParaseBetween参数错误");
            }
            try
            {
                int.Parse(valueArr[0]);
                int.Parse(valueArr[1]);
            }
            catch
            {
                throw new NotImplementedException("ParaseBetween参数只能为数字");
            }
            Expression expression = Expression.Constant(true, typeof(bool));
            //开始位置
            Expression startvalue = Expression.Constant(int.Parse(valueArr[0]));
            Expression start = Expression.GreaterThanOrEqual(key, Expression.Convert(startvalue, key.Type));

            Expression endvalue = Expression.Constant(int.Parse(valueArr[1]));
            Expression end = Expression.GreaterThanOrEqual(key, Expression.Convert(endvalue, key.Type));
            return Expression.AndAlso(start, end);
        }


        /// <summary>
        /// In
        /// </summary>
        /// <param name="parameter"></param>
        /// <param name="filterInfo"></param>
        /// <returns></returns>
        private static Expression ParaseIn(ParameterExpression parameter, FilterInfo filterInfo)
        {
            ParameterExpression p = parameter;
            Expression key = Expression.Property(p, filterInfo.Key);
            var valueArr = filterInfo.Value.Split(',');
            Expression expression = Expression.Constant(true, typeof(bool));
            foreach (var itemVal in valueArr)
            {
                Expression value = Expression.Constant(itemVal);
                Expression right = Expression.Equal(key, Expression.Convert(value, key.Type));

                expression = Expression.Or(expression, right);
            }
            return expression;
        }
    }
}
